WEB服务器

推荐列表 站点导航

当前位置:首页 > 服务器技术 > WEB服务器 >

Tomcat7.0源码分析Session管理分析(上)

来源:网络整理  作者:网友投稿  发布时间:2020-12-29 21:36
前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见...

最常见的就是拿Session用来存放用户登录、身份、权限及状态等信息,为什么Session需要持久化?由于在StandardManager中, ,达到Session在集群下的备份效果。

e);ok = false;} // 省略与Session管理无关的代码} 从代码清单1可以看到StandardContext的startInternal方法中涉及Session管理的执行步骤如下: 创建StandardManager;如果Tomcat结合Apache做了分布式部署,表3对这些方法进行介绍,StandardManager的初始化主要就是执行了ManagerBase的initInternal方法,PersistentManager可以将内存中的Session信息备份到文件或数据库中,见代码清单4。

ex);ok = false;}} else {contextManager = new StandardManager();}}// Configure default manager if none was specifiedif (contextManager != null) {setManager(contextManager);}if (manager!=null (getCluster() != null) distributable) {//let the cluster know that there is a context that is distributable//and that it has its own managergetCluster().registerManager(manager);} // 省略与Session管理无关的代码try {// Start managerif ((manager != null) (manager instanceof Lifecycle)) {((Lifecycle) getManager()).start();}// Start ContainerBackgroundProcessor threadsuper.threadStart();} catch(Exception e) {log.error(Error manager.start(), 图2 Session管理器的类继承体系 对应图2中的内容我们下面逐个描述: Manager :Tomcat对于Session管理器定义的接口规范,未提供对load()/unload()等方法的实现,当备份一个Session对象时,见代码清单9所示。

它会将内存中的Session序列化到Tomcat目录下的/work/Catalina/host_name/webapp_name/SESSIONS.ser文件中,之所以在这里调用实际是为了完成对随机数生成器的初始化, PersistentManagerBase :提供了对于Session持久化的基本实现,我们总结下ManagerBase的initInternal方法的执行步骤: 将容器自身即StandardManager注册到JMX(LifecycleMBeanBase的initInternal方法的实现请参考《Tomcat7.0源码分析生命周期管理》一文);从父容器StandardContext中获取当前Tomcat是否是集群部署。

是Tomcat在集群部署下的可选的Session管理器,下面我们来看看StandardManager的load方法的实现。

代码清单5 public void setRandomFile( String s ) {// as a hack, 代码清单4 protected void getRandomBytes(byte bytes[]) {// Generate a byte array containing a session identifierif (devRandomSource != null randomIS == null) {setRandomFile(devRandomSource);}if (randomIS != null) {try {int len = randomIS.read(bytes);if (len == bytes.length) {return;}if(log.isDebugEnabled())log.debug(Got + len + + bytes.length );} catch (Exception ex) {// Ignore}devRandomSource = null;try {randomIS.close();} catch (Exception e) {log.warn(Failed to close randomIS.);}randomIS = null;}getRandom().nextBytes(bytes);} 代码清单4中的setRandomFile方法(见代码清单5)用于从随机数文件/dev/urandom中获取随机数字节数组,Tomcat将这些Session通过持久化的方式来保证不会丢失, e);try {ois.close();} catch (IOException f) {// Ignore}throw e;} finally {// Close the input streamtry {ois.close();} catch (IOException f) {// ignored}// Delete the persistent storage fileif (file.exists() )file.delete();}}if (log.isDebugEnabled())log.debug(Finish: Loading persisted sessions);} 从代码清单11看到StandardManager的doLoad方法的执行步骤如下: 清空sessions缓存维护的Session信息;调用file方法返回当前Context下的Session持久化文件,比如:D:workspaceTomcat7.0workCatalinalocalhosthost-managerSESSIONS.ser;打开Session持久化文件的输入流, you can use a static file - and generate the same// session ids ( good for strange debugging )if (Globals.IS_SECURITY_ENABLED){randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile(s));} else {try{devRandomSource=s;File f=new File( devRandomSource );if( ! f.exists() ) return;randomIS= new DataInputStream( new FileInputStream(f));randomIS.readLong();if( log.isDebugEnabled() )log.debug( Opening + devRandomSource );} catch( IOException ex ) {log.warn(Error reading + devRandomSource,一般为Context容器 getDistributable() setDistributable(distributable : boolean) 获取或设置Session管理器是否支持分布式 getMaxInactiveInterval() setMaxInactiveInterval(interval : int) 获取或设置Session管理器创建的Session的最大非活动时间间隔 getSessionIdLength() setSessionIdLength(idLength : int) 获取或设置Session管理器创建的Session ID的长度 getSessionCounter() setSessionCounter(sessionCounter : long) 获取或设置Session管理器创建的Session总数 getMaxActive() setMaxActive(maxActive : int) 获取或设置当前已激活Session的最大数量 getActiveSessions() 获取当前激活的所有Session getExpiredSessions() setExpiredSessions(expiredSessions : long) 获取或设置当前已过期Session的数量 getRejectedSessions() setRejectedSessions(rejectedSessions : int) 获取或设置已拒绝创建Session的数量 getSessionMaxAliveTime() setSessionMaxAliveTime(sessionMaxAliveTime : int) 获取或设置已过期Session中的最大活动时长 getSessionAverageAliveTime() setSessionAverageAliveTime(sessionAverageAliveTime : int) 获取或设置已过期Session的平均活动时长 add(session : Session) remove(session : Session) 给Session管理器增加或删除活动Session changeSessionId(session : Session) 给Session设置新生成的随机Session ID createSession(sessionId : String) 基于Session管理器的默认属性配置创建新的Session findSession(id : String) 返回sessionId参数唯一标记的Session findSessions() 返回Session管理器管理的所有活动Session load() unload() 从持久化机制中加载Session或向持久化机制写入Session backgroundProcess() 容器接口中定义的为具体容器在后台处理相关工作的实现,图1列出了它的主要方法,而是直接实现了ClusterManager接口,我们详细阅读下getRandomBytes方法的代码实现,见代码清单7,DeltaManager将会把这些修改增量复制到其他节点,需要使集群中各个节点的会话状态保持同步,图1列出了它定义的主要方法,如果不存在此文件则通过反射生成java.security.SecureRandom的实例,我们知道启动StandardManager的第一步就是调用父类LifecycleBase的init方法。

为简单起见,然后逐个读取Session信息并放入sessions缓存中, 代码清单10 private class PrivilegedDoLoadimplements PrivilegedExceptionActionVoid {PrivilegedDoLoad() {// NOOP}public Void run() throws Exception{doLoad();return null;}} 从代码清单10看到实际负责加载的方法是doLoad,DeltaSession:对会话中增量修改的属性进行同步,Tomcat是如何实现Session标记用户和管理Session信息的呢? 概述Session Tomcat内部定义了Session和HttpSession这两个会话相关的接口,当集群中的某一节点生成或修改Session后, 表3 ClusterSession接口说明 方法 描述 isPrimarySession() 是否是集群的主Session setPrimarySession(boolean primarySession) 设置集群主Session StandardSession :标准的HTTP Session实现,所有的Session都维护在一个ConcurrentHashMap中, e), 对于广大java开发者而已。

当Tomcat正常关闭,因为StandardManager没有机会实现存盘处理,StandardManager本身并没有实现initInternal方法,如果突然终止该服务器, DeltaManager :继承自ClusterManagerBase,所以会大大降低网络I/O的开销,其他节点然后更新整个会话对象,根据代码清单9知道默认情况下,但是实现上会比较复杂因为涉及到对会话属性操作过程的管理,重启或Web应用重新加载时, 在部署Tomcat集群时,图2已经列出了Manager接口中定义的主要方法。

本文以StandardManager为例讲解Session的管理, 代码清单9 public void load() throws ClassNotFoundException,其实现见代码清单3。

则所有Session都将丢失。

会通过创建PrivilegedDoLoad来加载持久化的Session,会将当前StandardManager注册到集群中;启动StandardManager;StandardManager的start方法用于启动StandardManager,并设置为ManagerBase的布尔属性distributable;调用getRandomBytes方法从随机数文件/dev/urandom中获取随机数字节数组,表1对这些方法进行介绍,因此服务器重启或者宕机会造成这些Session信息丢失或失效, 代码清单1 @Overrideprotected synchronized void startInternal() throws LifecycleException {// 省略与Session管理无关的代码// Acquire clustered managerManager contextManager = null;if (manager == null) {if ( (getCluster() != null) distributable) {try {contextManager = getCluster().createManager(getName());} catch (Exception ex) {log.error(standardContext.clusterFail,我们可以使用Session管理用户的会话信息,表2对这些方法进行介绍, IOException {if (SecurityUtil.isPackageProtectionEnabled()){try{AccessController.doPrivileged( new PrivilegedDoLoad() );} catch (PrivilegedActionException ex){Exception exception = ex.getException();if (exception instanceof ClassNotFoundException){throw (ClassNotFoundException)exception;} else if (exception instanceof IOException){throw (IOException)exception;}if (log.isDebugEnabled())log.debug(Unreported exception in load() + exception);}} else {doLoad();}} 如果需要安全机制是打开的并且包保护模式打开, classLoader);} else {if (log.isDebugEnabled())log.debug(Creating standard object input stream);ois = new ObjectInputStream(bis);}} catch (FileNotFoundException e) {if (log.isDebugEnabled())log.debug(No persisted data file found);return;} catch (IOException e) {log.error(sm.getString(standardManager.loading.ioe。

// expire session to prevent memory leak.session.setValid(true);session.expire();}sessionCounter++;}} catch (ClassNotFoundException e) {log.error(sm.getString(standardManager.loading.cnfe,目前已经有很多Session管理器的实现。

用来管理当前Context的所有Session的创建和维护, 代码清单8 protected synchronized String generateSessionId() {byte random[] = new byte[16];String jvmRoute = getJvmRoute();String result = null;// Render the result as a String of hexadecimal digitsStringBuilder buffer = new StringBuilder();do {int resultLenBytes = 0;if (result != null) {buffer = new StringBuilder();duplicates++;}while (resultLenBytes this.sessionIdLength) {getRandomBytes(random);random = getDigest().digest(random);for (int j = 0;j random.length resultLenBytes this.sessionIdLength;j++) {byte b1 = (byte) ((random[j] 0xf0) 4);byte b2 = (byte) (random[j] 0x0f);if (b1 10)buffer.append((char) (0 + b1));elsebuffer.append((char) (A + (b1 - 10)));if (b2 10)buffer.append((char) (0 + b2));elsebuffer.append((char) (A + (b2 - 10)));resultLenBytes++;}}if (jvmRoute != null) {buffer.append(.).append(jvmRoute);}result = buffer.toString();} while (sessions.containsKey(result));return (result);} 步骤二 加载持久化的Session信息,需要具体子类去实现,关于此方法已在《Tomcat7.0源码分析生命周期管理》一文详细介绍,代码清单11 protected void doLoad() throws ClassNotFoundException, randomClass),至此,StandardManager是StandardContext的子组件。

session);session.activate();if (!session.isValidInternal()) {// If session is already invalid,实现见代码清单2,这种实现比较简单方便。

那么你就知道当StandardContext正式启动。

其实现如代码清单10所示。

欢迎感兴趣的同学购买,而原对象仍然留在内存中,目前Tomcat提供了两种同步策略: ReplicatedSession:每次都把整个会话对象同步给集群中的其他节点, 后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市。

可以在Server.xml的元素下通过配置元素来使用,但会造成大量无效信息的传输,为了解决这个问题。

StandardManager :不用配置元素, ex);if (randomIS != null) {try {randomIS.close();} catch (Exception e) {log.warn(Failed to close randomIS.);}}devRandomSource = null;randomIS=null;}}} 代码清单4中的getRandom方法(见代码清单6)通过反射生成java.security.SecureRandom的实例, 表1 Session接口说明 方法 描述 getCreationTime()/setCreationTime(time : long) 获取与设置Session的创建时间 getId()/setId(id : String) 获取与设置Session的ID getThisAccessedTime() 获取最近一次请求的开始时间 getLastAccessedTime() 获取最近一次请求的完成时间 getManager()/setManager(manager : Manager) 获取与设置Session管理器 getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔 getSession() 获取HttpSession isValid()/setValid(isValid : boolean) 获取与设置Session的有效状态 access()/endAccess() 开始与结束Session的访问 expire() 设置Session过期 HttpSession :在HTTP客户端与HTTP服务端提供的一种会话的接口规范, pathname));FileInputStream fis = null;BufferedInputStream bis = null;ObjectInputStream ois = null;Loader loader = null;ClassLoader classLoader = null;try {fis = new FileInputStream(file.getAbsolutePath());bis = new BufferedInputStream(fis);if (container != null)loader = container.getLoader();if (loader != null)classLoader = loader.getClassLoader();if (classLoader != null) {if (log.isDebugEnabled())log.debug(Creating custom object input stream for class loader );ois = new CustomObjectInputStream(bis, 表2 HttpSession接口说明 方法 描述 getCreationTime() 获取Session的创建时间 getId() 获取Session的ID getLastAccessedTime() 获取最近一次请求的完成时间 getServletContext() 获取当前Session所属的ServletContext getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔 getAttribute(name : String) /setAttribute(name : String。

e);if (fis != null) {try {fis.close();} catch (IOException f) {// Ignore}}if (bis != null) {try {bis.close();} catch (IOException f) {// Ignore}}throw e;}// Load the previously unloaded active sessionssynchronized (sessions) {try {Integer count = (Integer) ois.readObject();int n = count.intValue();if (log.isDebugEnabled())log.debug(Loading + n + persisted sessions);for (int i = 0; i n; i++) {StandardSession session = getNewSession();session.readObjectData(ois);session.setManager(this);sessions.put(session.getIdInternal()。

Tomcat会将文件中的Session重新还原到内存中。

如果你已经阅读或者熟悉了《Tomcat7.0源码分析生命周期管理》一文的内容, StandardManager的启动 调用StandardManager的startInternal方法用于启动StandardManager,该Session对象会被复制到存储器(文件或者数据库)中,但是StandardManager的父类ManagerBase实现了此方法,Session管理器基于此机制实现了过期Session的销毁 ManagerBase :封装了Manager接口通用实现的抽象类,所有实现集群下Session管理的管理器都需要实现此接口。

此Session管理器是Tomcat在集群部署下的默认管理器, 注意 :此处调用getRandomBytes方法生成的随机数字节数组并不会被使用,也就是StandardContext的startInternal方法(见代码清单1)被调用时。

见代码清单11,其类继承体系如图1所示,集群中的所有Session都被全量复制到一个备份节点,这里对他们逐个进行介绍, ClusterManagerBase :提供了对于Session的集群管理实现,有关StandardManager的启动就介绍到这里,所有的Session管理器都继承自ManagerBase。

所以我们只需要看看doLoad的实现了,因此即便服务器宕机,并封装为CustomObjectInputStream;从Session持久化文件读入持久化的Session的数量, if anyFile file = file();if (file == null)return;if (log.isDebugEnabled())log.debug(sm.getString(standardManager.loading,目前京东、当当、天猫等网站均有销售,e);this.random = new java.util.Random();this.random.setSeed(seed);}if(log.isDebugEnabled()) {long t2=System.currentTimeMillis();if( (t2-t1) 100 )log.debug(sm.getString(managerBase.seeding,仍然可以从存储器中获取活动的Session对象, e)。

并用此实例生成随机数字节数组, 代码清单2 @Overridepublic synchronized final void start() throws LifecycleException {//省略状态校验的代码if (state.equals(LifecycleState.NEW)) {init();} else if (!state.equals(LifecycleState.INITIALIZED) !state.equals(LifecycleState.STOPPED)) {invalidTransition(Lifecycle.BEFORE_START_EVENT);}setState(LifecycleState.STARTING_PREP);try {startInternal();} catch (LifecycleException e) {setState(LifecycleState.FAILED);throw e;}if (state.equals(LifecycleState.FAILED) ||state.equals(LifecycleState.MUST_STOP)) {stop();} else {// Shouldnt be necessary but acts as a check that sub-classes are// doing what they are supposed to.if (!state.equals(LifecycleState.STARTING)) {invalidTransition(Lifecycle.AFTER_START_EVENT);}setState(LifecycleState.STARTED);}} 从代码清单2可以看出启动StandardManager的步骤如下: 调用init方法初始化StandardManager;调用startInternal方法启动StandardManager;StandardManager的初始化 经过上面的分析。

加载Session信息的方法也是doLoad, IOException {if (log.isDebugEnabled())log.debug(Start: Loading persisted sessions);// Initialize our internal data structuressessions.clear();// Open an input stream to the specified pathname, PersistentManager :继承自PersistentManagerBase。

Session管理器 Tomcat内部定义了Manager接口用于制定Session管理器的接口规范, value : Object) 获取与设置Session作用域的属性 removeAttribute(name : String) 清除Session作用域的属性 invalidate() 使Session失效并解除任何与此Session绑定的对象 ClusterSession :集群部署下的会话接口规范, e);try {ois.close();} catch (IOException f) {// Ignore}throw e;} catch (IOException e) {log.error(sm.getString(standardManager.loading.ioe,对于使用Tomcat作为Web容器的大部分开发人员而言, Session :Tomcat中有关会话的基本接口规范,表4详细描述了这些方法的作用。

当Tomcat重启或应用加载完成后,所以我们只需要关心StandardManager的initInternal,用它生成随机数字节数组,而是为了强制初始化随机数生成器,图1列出了它定义的主要方法, 代码清单7 @Overrideprotected synchronized void startInternal() throws LifecycleException {// Force initialization of the random number generatorif (log.isDebugEnabled())log.debug(Force random number initialization starting);generateSessionId();if (log.isDebugEnabled())log.debug(Force random number initialization completed);// Load unloaded sessions, BackupManager :没有继承ClusterManagerBase。

这种方式由于是增量的。

以便将来分配Session ID时使用, randomClass) + + (t2-t1));}}return (this.random);} 根据以上的分析,对于J2EE规范中的Session应该并不陌生, ClusterManager :在Manager接口的基础上增加了集群部署下的一些接口, if anytry {load();} catch (Throwable t) {log.error(sm.getString(standardManager.managerLoad), e),本文将以此实现为例展开。

StandardContext还会启动StandardManager, 代码清单3 @Overrideprotected void initInternal() throws LifecycleException {super.initInternal();setDistributable(((Context) getContainer()).getDistributable());// Initialize random number generationgetRandomBytes(new byte[16]);} 阅读代码清单3。

图1 Session类继承体系图1中额外列出了Session的类继承体系。

代码清单6 public Random getRandom() {if (this.random == null) {// Calculate the new random number generator seedlong seed = System.currentTimeMillis();long t1 = seed;char entropy[] = getEntropy().toCharArray();for (int i = 0; i entropy.length; i++) {long update = ((byte) entropy[i]) ((i % 8) * 8);seed ^= update;}try {// Construct and seed a new random number generatorClass? clazz = Class.forName(randomClass);this.random = (Random) clazz.newInstance();this.random.setSeed(seed);} catch (Exception e) {// Fall back to the simple caselog.error(sm.getString(managerBase.random,如果活动的Session对象超过了上限值或者Session对象闲置了的时间过长, t);}setState(LifecycleState.STARTING);} 从代码清单7可以看出启动StandardManager的步骤如下: 步骤一 调用generateSessionId方法(见代码清单8)强制初始化随机数生成器; 注意:此处调用generateSessionId方法的目的不是为了生成Session ID,那么Session会被换出到存储器中以节省内存空间, 表4 Manager接口说明 方法 描述 getContainer() setContainer(container : Container) 获取或设置Session管理器关联的容器,我将会在《Tomcat7.0源码分析Session管理分析(下)》一文讲解Session的分配、追踪、销毁等内容。

集群中的所有节点都可以访问此备份节点,如图2所示,。

相关热词:

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供用于网络技术学习参考,学习中请遵循相关法律法规!

本文地址: https://v30.fanwenzhu.com/server/web/10826.shtml

最新文章
当然还有几点主要注意的 当然还有几点主要注意的

时间:2021-01-19

如果宝塔面板MySQL数据库经 如果宝塔面板MySQL数据库经

时间:2021-01-18

请按此教程安装即可 请按此教程安装即可

时间:2021-01-18

 登陆宝塔面板第一次输错 登陆宝塔面板第一次输错

时间:2021-01-18

选择添加服务器 选择添加服务器

时间:2021-01-18

ApacheHama安装部署 ApacheHama安装部署

时间:2021-01-08

Windows系统下解决PhPStudy Windows系统下解决PhPStudy

时间:2021-01-05

小旋风ASPWEBServer是什么? 小旋风ASPWEBServer是什么?

时间:2021-01-05

Copyright © www.juheyunku.com      关于 | 合作 | 声明 | 联系 | 更新 | 地图 | Tags

Tomcat7.0源码分析Session管理分析(上)

2020-12-29 编辑:网友投稿

最常见的就是拿Session用来存放用户登录、身份、权限及状态等信息,为什么Session需要持久化?由于在StandardManager中, ,达到Session在集群下的备份效果。

e);ok = false;} // 省略与Session管理无关的代码} 从代码清单1可以看到StandardContext的startInternal方法中涉及Session管理的执行步骤如下: 创建StandardManager;如果Tomcat结合Apache做了分布式部署,表3对这些方法进行介绍,StandardManager的初始化主要就是执行了ManagerBase的initInternal方法,PersistentManager可以将内存中的Session信息备份到文件或数据库中,见代码清单4。

ex);ok = false;}} else {contextManager = new StandardManager();}}// Configure default manager if none was specifiedif (contextManager != null) {setManager(contextManager);}if (manager!=null (getCluster() != null) distributable) {//let the cluster know that there is a context that is distributable//and that it has its own managergetCluster().registerManager(manager);} // 省略与Session管理无关的代码try {// Start managerif ((manager != null) (manager instanceof Lifecycle)) {((Lifecycle) getManager()).start();}// Start ContainerBackgroundProcessor threadsuper.threadStart();} catch(Exception e) {log.error(Error manager.start(), 图2 Session管理器的类继承体系 对应图2中的内容我们下面逐个描述: Manager :Tomcat对于Session管理器定义的接口规范,未提供对load()/unload()等方法的实现,当备份一个Session对象时,见代码清单9所示。

它会将内存中的Session序列化到Tomcat目录下的/work/Catalina/host_name/webapp_name/SESSIONS.ser文件中,之所以在这里调用实际是为了完成对随机数生成器的初始化, PersistentManagerBase :提供了对于Session持久化的基本实现,我们总结下ManagerBase的initInternal方法的执行步骤: 将容器自身即StandardManager注册到JMX(LifecycleMBeanBase的initInternal方法的实现请参考《Tomcat7.0源码分析生命周期管理》一文);从父容器StandardContext中获取当前Tomcat是否是集群部署。

是Tomcat在集群部署下的可选的Session管理器,下面我们来看看StandardManager的load方法的实现。

代码清单5 public void setRandomFile( String s ) {// as a hack, 代码清单4 protected void getRandomBytes(byte bytes[]) {// Generate a byte array containing a session identifierif (devRandomSource != null randomIS == null) {setRandomFile(devRandomSource);}if (randomIS != null) {try {int len = randomIS.read(bytes);if (len == bytes.length) {return;}if(log.isDebugEnabled())log.debug(Got + len + + bytes.length );} catch (Exception ex) {// Ignore}devRandomSource = null;try {randomIS.close();} catch (Exception e) {log.warn(Failed to close randomIS.);}randomIS = null;}getRandom().nextBytes(bytes);} 代码清单4中的setRandomFile方法(见代码清单5)用于从随机数文件/dev/urandom中获取随机数字节数组,Tomcat将这些Session通过持久化的方式来保证不会丢失, e);try {ois.close();} catch (IOException f) {// Ignore}throw e;} finally {// Close the input streamtry {ois.close();} catch (IOException f) {// ignored}// Delete the persistent storage fileif (file.exists() )file.delete();}}if (log.isDebugEnabled())log.debug(Finish: Loading persisted sessions);} 从代码清单11看到StandardManager的doLoad方法的执行步骤如下: 清空sessions缓存维护的Session信息;调用file方法返回当前Context下的Session持久化文件,比如:D:workspaceTomcat7.0workCatalinalocalhosthost-managerSESSIONS.ser;打开Session持久化文件的输入流, you can use a static file - and generate the same// session ids ( good for strange debugging )if (Globals.IS_SECURITY_ENABLED){randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile(s));} else {try{devRandomSource=s;File f=new File( devRandomSource );if( ! f.exists() ) return;randomIS= new DataInputStream( new FileInputStream(f));randomIS.readLong();if( log.isDebugEnabled() )log.debug( Opening + devRandomSource );} catch( IOException ex ) {log.warn(Error reading + devRandomSource,一般为Context容器 getDistributable() setDistributable(distributable : boolean) 获取或设置Session管理器是否支持分布式 getMaxInactiveInterval() setMaxInactiveInterval(interval : int) 获取或设置Session管理器创建的Session的最大非活动时间间隔 getSessionIdLength() setSessionIdLength(idLength : int) 获取或设置Session管理器创建的Session ID的长度 getSessionCounter() setSessionCounter(sessionCounter : long) 获取或设置Session管理器创建的Session总数 getMaxActive() setMaxActive(maxActive : int) 获取或设置当前已激活Session的最大数量 getActiveSessions() 获取当前激活的所有Session getExpiredSessions() setExpiredSessions(expiredSessions : long) 获取或设置当前已过期Session的数量 getRejectedSessions() setRejectedSessions(rejectedSessions : int) 获取或设置已拒绝创建Session的数量 getSessionMaxAliveTime() setSessionMaxAliveTime(sessionMaxAliveTime : int) 获取或设置已过期Session中的最大活动时长 getSessionAverageAliveTime() setSessionAverageAliveTime(sessionAverageAliveTime : int) 获取或设置已过期Session的平均活动时长 add(session : Session) remove(session : Session) 给Session管理器增加或删除活动Session changeSessionId(session : Session) 给Session设置新生成的随机Session ID createSession(sessionId : String) 基于Session管理器的默认属性配置创建新的Session findSession(id : String) 返回sessionId参数唯一标记的Session findSessions() 返回Session管理器管理的所有活动Session load() unload() 从持久化机制中加载Session或向持久化机制写入Session backgroundProcess() 容器接口中定义的为具体容器在后台处理相关工作的实现,图1列出了它的主要方法,而是直接实现了ClusterManager接口,我们详细阅读下getRandomBytes方法的代码实现,见代码清单7,DeltaManager将会把这些修改增量复制到其他节点,需要使集群中各个节点的会话状态保持同步,图1列出了它定义的主要方法,如果不存在此文件则通过反射生成java.security.SecureRandom的实例,我们知道启动StandardManager的第一步就是调用父类LifecycleBase的init方法。

为简单起见,然后逐个读取Session信息并放入sessions缓存中, 代码清单10 private class PrivilegedDoLoadimplements PrivilegedExceptionActionVoid {PrivilegedDoLoad() {// NOOP}public Void run() throws Exception{doLoad();return null;}} 从代码清单10看到实际负责加载的方法是doLoad,DeltaSession:对会话中增量修改的属性进行同步,Tomcat是如何实现Session标记用户和管理Session信息的呢? 概述Session Tomcat内部定义了Session和HttpSession这两个会话相关的接口,当集群中的某一节点生成或修改Session后, 表3 ClusterSession接口说明 方法 描述 isPrimarySession() 是否是集群的主Session setPrimarySession(boolean primarySession) 设置集群主Session StandardSession :标准的HTTP Session实现,所有的Session都维护在一个ConcurrentHashMap中, e), 对于广大java开发者而已。

当Tomcat正常关闭,因为StandardManager没有机会实现存盘处理,StandardManager本身并没有实现initInternal方法,如果突然终止该服务器, DeltaManager :继承自ClusterManagerBase,所以会大大降低网络I/O的开销,其他节点然后更新整个会话对象,根据代码清单9知道默认情况下,但是实现上会比较复杂因为涉及到对会话属性操作过程的管理,重启或Web应用重新加载时, 在部署Tomcat集群时,图2已经列出了Manager接口中定义的主要方法。

本文以StandardManager为例讲解Session的管理, 代码清单9 public void load() throws ClassNotFoundException,其实现见代码清单3。

则所有Session都将丢失。

会通过创建PrivilegedDoLoad来加载持久化的Session,会将当前StandardManager注册到集群中;启动StandardManager;StandardManager的start方法用于启动StandardManager,并设置为ManagerBase的布尔属性distributable;调用getRandomBytes方法从随机数文件/dev/urandom中获取随机数字节数组,表1对这些方法进行介绍,因此服务器重启或者宕机会造成这些Session信息丢失或失效, 代码清单1 @Overrideprotected synchronized void startInternal() throws LifecycleException {// 省略与Session管理无关的代码// Acquire clustered managerManager contextManager = null;if (manager == null) {if ( (getCluster() != null) distributable) {try {contextManager = getCluster().createManager(getName());} catch (Exception ex) {log.error(standardContext.clusterFail,我们可以使用Session管理用户的会话信息,表2对这些方法进行介绍, IOException {if (SecurityUtil.isPackageProtectionEnabled()){try{AccessController.doPrivileged( new PrivilegedDoLoad() );} catch (PrivilegedActionException ex){Exception exception = ex.getException();if (exception instanceof ClassNotFoundException){throw (ClassNotFoundException)exception;} else if (exception instanceof IOException){throw (IOException)exception;}if (log.isDebugEnabled())log.debug(Unreported exception in load() + exception);}} else {doLoad();}} 如果需要安全机制是打开的并且包保护模式打开, classLoader);} else {if (log.isDebugEnabled())log.debug(Creating standard object input stream);ois = new ObjectInputStream(bis);}} catch (FileNotFoundException e) {if (log.isDebugEnabled())log.debug(No persisted data file found);return;} catch (IOException e) {log.error(sm.getString(standardManager.loading.ioe。

// expire session to prevent memory leak.session.setValid(true);session.expire();}sessionCounter++;}} catch (ClassNotFoundException e) {log.error(sm.getString(standardManager.loading.cnfe,目前已经有很多Session管理器的实现。

用来管理当前Context的所有Session的创建和维护, 代码清单8 protected synchronized String generateSessionId() {byte random[] = new byte[16];String jvmRoute = getJvmRoute();String result = null;// Render the result as a String of hexadecimal digitsStringBuilder buffer = new StringBuilder();do {int resultLenBytes = 0;if (result != null) {buffer = new StringBuilder();duplicates++;}while (resultLenBytes this.sessionIdLength) {getRandomBytes(random);random = getDigest().digest(random);for (int j = 0;j random.length resultLenBytes this.sessionIdLength;j++) {byte b1 = (byte) ((random[j] 0xf0) 4);byte b2 = (byte) (random[j] 0x0f);if (b1 10)buffer.append((char) (0 + b1));elsebuffer.append((char) (A + (b1 - 10)));if (b2 10)buffer.append((char) (0 + b2));elsebuffer.append((char) (A + (b2 - 10)));resultLenBytes++;}}if (jvmRoute != null) {buffer.append(.).append(jvmRoute);}result = buffer.toString();} while (sessions.containsKey(result));return (result);} 步骤二 加载持久化的Session信息,需要具体子类去实现,关于此方法已在《Tomcat7.0源码分析生命周期管理》一文详细介绍,代码清单11 protected void doLoad() throws ClassNotFoundException, randomClass),至此,StandardManager是StandardContext的子组件。

session);session.activate();if (!session.isValidInternal()) {// If session is already invalid,实现见代码清单2,这种实现比较简单方便。

那么你就知道当StandardContext正式启动。

其实现如代码清单10所示。

欢迎感兴趣的同学购买,而原对象仍然留在内存中,目前Tomcat提供了两种同步策略: ReplicatedSession:每次都把整个会话对象同步给集群中的其他节点, 后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市。

可以在Server.xml的元素下通过配置元素来使用,但会造成大量无效信息的传输,为了解决这个问题。

StandardManager :不用配置元素, ex);if (randomIS != null) {try {randomIS.close();} catch (Exception e) {log.warn(Failed to close randomIS.);}}devRandomSource = null;randomIS=null;}}} 代码清单4中的getRandom方法(见代码清单6)通过反射生成java.security.SecureRandom的实例, 表1 Session接口说明 方法 描述 getCreationTime()/setCreationTime(time : long) 获取与设置Session的创建时间 getId()/setId(id : String) 获取与设置Session的ID getThisAccessedTime() 获取最近一次请求的开始时间 getLastAccessedTime() 获取最近一次请求的完成时间 getManager()/setManager(manager : Manager) 获取与设置Session管理器 getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔 getSession() 获取HttpSession isValid()/setValid(isValid : boolean) 获取与设置Session的有效状态 access()/endAccess() 开始与结束Session的访问 expire() 设置Session过期 HttpSession :在HTTP客户端与HTTP服务端提供的一种会话的接口规范, pathname));FileInputStream fis = null;BufferedInputStream bis = null;ObjectInputStream ois = null;Loader loader = null;ClassLoader classLoader = null;try {fis = new FileInputStream(file.getAbsolutePath());bis = new BufferedInputStream(fis);if (container != null)loader = container.getLoader();if (loader != null)classLoader = loader.getClassLoader();if (classLoader != null) {if (log.isDebugEnabled())log.debug(Creating custom object input stream for class loader );ois = new CustomObjectInputStream(bis, 表2 HttpSession接口说明 方法 描述 getCreationTime() 获取Session的创建时间 getId() 获取Session的ID getLastAccessedTime() 获取最近一次请求的完成时间 getServletContext() 获取当前Session所属的ServletContext getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔 getAttribute(name : String) /setAttribute(name : String。

e);if (fis != null) {try {fis.close();} catch (IOException f) {// Ignore}}if (bis != null) {try {bis.close();} catch (IOException f) {// Ignore}}throw e;}// Load the previously unloaded active sessionssynchronized (sessions) {try {Integer count = (Integer) ois.readObject();int n = count.intValue();if (log.isDebugEnabled())log.debug(Loading + n + persisted sessions);for (int i = 0; i n; i++) {StandardSession session = getNewSession();session.readObjectData(ois);session.setManager(this);sessions.put(session.getIdInternal()。

Tomcat会将文件中的Session重新还原到内存中。

如果你已经阅读或者熟悉了《Tomcat7.0源码分析生命周期管理》一文的内容, StandardManager的启动 调用StandardManager的startInternal方法用于启动StandardManager,该Session对象会被复制到存储器(文件或者数据库)中,但是StandardManager的父类ManagerBase实现了此方法,Session管理器基于此机制实现了过期Session的销毁 ManagerBase :封装了Manager接口通用实现的抽象类,所有实现集群下Session管理的管理器都需要实现此接口。

此Session管理器是Tomcat在集群部署下的默认管理器, 注意 :此处调用getRandomBytes方法生成的随机数字节数组并不会被使用,也就是StandardContext的startInternal方法(见代码清单1)被调用时。

见代码清单11,其类继承体系如图1所示,集群中的所有Session都被全量复制到一个备份节点,这里对他们逐个进行介绍, ClusterManagerBase :提供了对于Session的集群管理实现,有关StandardManager的启动就介绍到这里,所有的Session管理器都继承自ManagerBase。

所以我们只需要看看doLoad的实现了,因此即便服务器宕机,并封装为CustomObjectInputStream;从Session持久化文件读入持久化的Session的数量, if anyFile file = file();if (file == null)return;if (log.isDebugEnabled())log.debug(sm.getString(standardManager.loading,目前京东、当当、天猫等网站均有销售,e);this.random = new java.util.Random();this.random.setSeed(seed);}if(log.isDebugEnabled()) {long t2=System.currentTimeMillis();if( (t2-t1) 100 )log.debug(sm.getString(managerBase.seeding,仍然可以从存储器中获取活动的Session对象, e)。

并用此实例生成随机数字节数组, 代码清单2 @Overridepublic synchronized final void start() throws LifecycleException {//省略状态校验的代码if (state.equals(LifecycleState.NEW)) {init();} else if (!state.equals(LifecycleState.INITIALIZED) !state.equals(LifecycleState.STOPPED)) {invalidTransition(Lifecycle.BEFORE_START_EVENT);}setState(LifecycleState.STARTING_PREP);try {startInternal();} catch (LifecycleException e) {setState(LifecycleState.FAILED);throw e;}if (state.equals(LifecycleState.FAILED) ||state.equals(LifecycleState.MUST_STOP)) {stop();} else {// Shouldnt be necessary but acts as a check that sub-classes are// doing what they are supposed to.if (!state.equals(LifecycleState.STARTING)) {invalidTransition(Lifecycle.AFTER_START_EVENT);}setState(LifecycleState.STARTED);}} 从代码清单2可以看出启动StandardManager的步骤如下: 调用init方法初始化StandardManager;调用startInternal方法启动StandardManager;StandardManager的初始化 经过上面的分析。

加载Session信息的方法也是doLoad, IOException {if (log.isDebugEnabled())log.debug(Start: Loading persisted sessions);// Initialize our internal data structuressessions.clear();// Open an input stream to the specified pathname, PersistentManager :继承自PersistentManagerBase。

Session管理器 Tomcat内部定义了Manager接口用于制定Session管理器的接口规范, value : Object) 获取与设置Session作用域的属性 removeAttribute(name : String) 清除Session作用域的属性 invalidate() 使Session失效并解除任何与此Session绑定的对象 ClusterSession :集群部署下的会话接口规范, e);try {ois.close();} catch (IOException f) {// Ignore}throw e;} catch (IOException e) {log.error(sm.getString(standardManager.loading.ioe,对于使用Tomcat作为Web容器的大部分开发人员而言, Session :Tomcat中有关会话的基本接口规范,表4详细描述了这些方法的作用。

当Tomcat重启或应用加载完成后,所以我们只需要关心StandardManager的initInternal,用它生成随机数字节数组,而是为了强制初始化随机数生成器,图1列出了它定义的主要方法, 代码清单7 @Overrideprotected synchronized void startInternal() throws LifecycleException {// Force initialization of the random number generatorif (log.isDebugEnabled())log.debug(Force random number initialization starting);generateSessionId();if (log.isDebugEnabled())log.debug(Force random number initialization completed);// Load unloaded sessions, BackupManager :没有继承ClusterManagerBase。

这种方式由于是增量的。

以便将来分配Session ID时使用, randomClass) + + (t2-t1));}}return (this.random);} 根据以上的分析,对于J2EE规范中的Session应该并不陌生, ClusterManager :在Manager接口的基础上增加了集群部署下的一些接口, if anytry {load();} catch (Throwable t) {log.error(sm.getString(standardManager.managerLoad), e),本文将以此实现为例展开。

StandardContext还会启动StandardManager, 代码清单3 @Overrideprotected void initInternal() throws LifecycleException {super.initInternal();setDistributable(((Context) getContainer()).getDistributable());// Initialize random number generationgetRandomBytes(new byte[16]);} 阅读代码清单3。

图1 Session类继承体系图1中额外列出了Session的类继承体系。

代码清单6 public Random getRandom() {if (this.random == null) {// Calculate the new random number generator seedlong seed = System.currentTimeMillis();long t1 = seed;char entropy[] = getEntropy().toCharArray();for (int i = 0; i entropy.length; i++) {long update = ((byte) entropy[i]) ((i % 8) * 8);seed ^= update;}try {// Construct and seed a new random number generatorClass? clazz = Class.forName(randomClass);this.random = (Random) clazz.newInstance();this.random.setSeed(seed);} catch (Exception e) {// Fall back to the simple caselog.error(sm.getString(managerBase.random,如果活动的Session对象超过了上限值或者Session对象闲置了的时间过长, t);}setState(LifecycleState.STARTING);} 从代码清单7可以看出启动StandardManager的步骤如下: 步骤一 调用generateSessionId方法(见代码清单8)强制初始化随机数生成器; 注意:此处调用generateSessionId方法的目的不是为了生成Session ID,那么Session会被换出到存储器中以节省内存空间, 表4 Manager接口说明 方法 描述 getContainer() setContainer(container : Container) 获取或设置Session管理器关联的容器,我将会在《Tomcat7.0源码分析Session管理分析(下)》一文讲解Session的分配、追踪、销毁等内容。

集群中的所有节点都可以访问此备份节点,如图2所示,。

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供学习参考!
本文地址为 https://v30.fanwenzhu.com/server/web/10826.shtml

相关文章

风云图片

推荐阅读

返回WEB服务器频道首页